#ifndef DW_MATHS_POINT_INL
#	define DW_MATHS_POINT_INL

namespace dw {
	namespace maths {

template< typename T, unsigned N >
inline Point< T, N >::Point()
{
}

template< typename T, unsigned N >
inline Point< T, N >::Point(const T* rhs)
{
	assert(rhs != 0);
	memcpy(this->p, rhs, sizeof (T) * N);
}

template< typename T, unsigned N >
inline Point< T, N >&								Point< T, N >::operator=(const Point< T, N >& rhs)
{
	memcpy(this->p, rhs.p, sizeof (T) * N);
	return *this;
}

template< typename T, unsigned N >
inline Point< T, N >&								Point< T, N >::operator+=(const Point< T, N >& rhs)
{
	for (unsigned i = 0; i < N; ++i)
		this->p[i] += rhs.p[i];
	return *this;
}

template< typename T, unsigned N >
inline Point< T, N >&								Point< T, N >::operator+=(const Vector< T, N >& rhs)
{
	for (unsigned i = 0; i < N; ++i)
		this->p[i] += rhs.p[i];
	return *this;
}

template< typename T, unsigned N >
inline Point< T, N >&								Point< T, N >::operator-=(const Vector< T, N >& rhs)
{
	for (unsigned i = 0; i < N; ++i)
		this->p[i] -= rhs.p[i];
	return *this;
}

template< typename T, unsigned N >
inline Point< T, N >&								Point< T, N >::operator*=(const T& rhs)
{
	for (unsigned i = 0; i < N; ++i)
		this->p[i] *= rhs;
	return *this;
}

template< typename T, unsigned N >
inline Point< T, N >&								Point< T, N >::operator/=(const T& rhs)
{
	for (unsigned i = 0; i < N; ++i)
		this->p[i] /= rhs;
	return *this;
}

template< typename T, unsigned N >
inline Vector< T, N >								Point< T, N >::toVector() const
{
	return Vector< T, N >(this->p);
}

template< typename T, unsigned N >
inline std::string Point< T, N >::toString() const
{
	std::stringstream ss;

	ss << "< ";
	for (unsigned i = 0; i < N; ++i)
	{
		ss << this->p[i];
		if (i < N - 1)
			ss << ", ";
	}
	ss << " >";
	return ss.str();
}



// Point< T, 2 > partial specialization



template< typename T > const Point< T, 2 > Point< T, 2 >::zero(0, 0);

template< typename T >
inline Point< T, 2 >::Point()
{
}

template< typename T >
inline Point< T, 2 >::Point(const T* rhs)
{
	if (!rhs) throw std::invalid_argument(__FUNCTION__);
	memcpy(this->p, rhs, sizeof (T) * 2);
}

template< typename T >
inline Point< T, 2 >::Point(const T& x, const T& y) :
	x(x), y(y)
{
}

template< typename T >
inline Point< T, 2 >&								Point< T, 2 >::operator=(const Point< T, 2 >& rhs)
{
	this->x = rhs.x;
	this->y = rhs.y;
	return *this;
}

template< typename T >
inline Point< T, 2 >&								Point< T, 2 >::operator+=(const Point< T, 2 >& rhs)
{
	this->x += rhs.x;
	this->y += rhs.y;
	return *this;
}

template< typename T >
inline Point< T, 2 >&								Point< T, 2 >::operator+=(const Vector< T, 2 >& rhs)
{
	this->x += rhs.x;
	this->y += rhs.y;
	return *this;
}

template< typename T >
inline Point< T, 2 >&								Point< T, 2 >::operator-=(const Vector< T, 2 >& rhs)
{
	this->x -= rhs.x;
	this->y -= rhs.y;
	return *this;
}

template< typename T >
inline Point< T, 2 >&								Point< T, 2 >::operator*=(const T& rhs)
{
	this->x *= rhs;
	this->y *= rhs;
	return *this;
}

template< typename T >
inline Point< T, 2 >&								Point< T, 2 >::operator/=(const T& rhs)
{
	this->x /= rhs;
	this->y /= rhs;
	return *this;
}

template< typename T >
inline std::string Point< T, 2 >::toString() const
{
	std::stringstream ss;

	ss << "<" << this->x << ", " <<  this->y << ">";
	return ss.str();
}



// Point< T, 3 > partial specialization



template< typename T > 
const Point< T, 3 >									Point< T, 3 >::zero(0, 0, 0);

template< typename T >
inline Point< T, 3 >::Point()
{
}

template< typename T >
inline Point< T, 3 >::Point(const T* rhs)
{
	if (!rhs) throw std::invalid_argument(__FUNCTION__);
	memcpy(this->p, rhs, sizeof (T) * 3);
}

template< typename T >
inline Point< T, 3 >::Point(const T& x, const T& y, const T& z) :
	x(x), y(y), z(z)
{
}

template< typename T >
inline Point< T, 3 >&								Point< T, 3 >::operator=(const Point< T, 3 >& rhs)
{
	this->x = rhs.x;
	this->y = rhs.y;
	this->z = rhs.z;
	return *this;
}

template< typename T >
inline Point< T, 3 >&								Point< T, 3 >::operator+=(const Point< T, 3 >& rhs)
{
	this->x += rhs.x;
	this->y += rhs.y;
	this->z += rhs.z;
	return *this;
}

template< typename T >
inline Point< T, 3 >&								Point< T, 3 >::operator+=(const Vector< T, 3 >& rhs)
{
	this->x += rhs.x;
	this->y += rhs.y;
	this->z += rhs.z;
	return *this;
}

template< typename T >
inline Point< T, 3 >&								Point< T, 3 >::operator-=(const Vector< T, 3 >& rhs)
{
	this->x -= rhs.x;
	this->y -= rhs.y;
	this->z -= rhs.z;
	return *this;
}

template< typename T >
inline Point< T, 3 >&								Point< T, 3 >::operator*=(const T& rhs)
{
	this->x *= rhs;
	this->y *= rhs;
	this->z *= rhs;
	return *this;
}

template< typename T >
inline Point< T, 3 >&								Point< T, 3 >::operator/=(const T& rhs)
{
	this->x /= rhs;
	this->y /= rhs;
	this->z /= rhs;
	return *this;
}

template< typename T >
inline std::string Point< T, 3 >::toString() const
{
	std::stringstream ss;

	ss << "<" << this->x << ", " <<  this->y << ", " << this->z << ">";
	return ss.str();
}

template< typename T >
inline Point< T, 3 >								Point< T, 3 >::transform(const Point< T, 3 >& p, const Matrix< T, 4, 4 >& m)
{
	Point< T, 4 >											tmp(p.x, p.y, p.z, 1);
	T div;

	tmp = m * tmp;
	div = 1 / tmp.w;
	return Point< T, 3 >(tmp.x * div, tmp.y * div, tmp.z * div);
}



// Point< T, 4 > partial specialization



template< typename T > 
const Point< T, 4 >									Point< T, 4 >::zero(0, 0, 0, 0);

template< typename T >
inline Point< T, 4 >::Point()
{
}

template< typename T >
inline Point< T, 4 >::Point(const T* rhs)
{
	if (!rhs) throw std::invalid_argument(__FUNCTION__);
	memcpy(this->p, rhs, sizeof (T) * 4);
}

template< typename T >
inline Point< T, 4 >::Point(const T& x, const T& y, const T& z, const T& w) :
	x(x), y(y), z(z), w(w)
{
}

template< typename T >
inline Point< T, 4 >&								Point< T, 4 >::operator=(const Point< T, 4 >& rhs)
{
	this->x = rhs.x;
	this->y = rhs.y;
	this->z = rhs.z;
	this->w = rhs.w;
	return *this;
}

template< typename T >
inline Point< T, 4 >&								Point< T, 4 >::operator+=(const Point< T, 4 >& rhs)
{
	this->x += rhs.x;
	this->y += rhs.y;
	this->z += rhs.z;
	this->w += rhs.w;
	return *this;
}

template< typename T >
inline Point< T, 4 >&								Point< T, 4 >::operator+=(const Vector< T, 4 >& rhs)
{
	this->x += rhs.x;
	this->y += rhs.y;
	this->z += rhs.z;
	this->w += rhs.w;
	return *this;
}

template< typename T >
inline Point< T, 4 >&								Point< T, 4 >::operator-=(const Vector< T, 4 >& rhs)
{
	this->x -= rhs.x;
	this->y -= rhs.y;
	this->z -= rhs.z;
	this->w -= rhs.w;
	return *this;
}

template< typename T >
inline Point< T, 4 >&								Point< T, 4 >::operator*=(const T& rhs)
{
	this->x *= rhs;
	this->y *= rhs;
	this->z *= rhs;
	this->w *= rhs;
	return *this;
}

template< typename T >
inline Point< T, 4 >&								Point< T, 4 >::operator/=(const T& rhs)
{
	this->x /= rhs;
	this->y /= rhs;
	this->z /= rhs;
	this->w /= rhs;
	return *this;
}

template< typename T >
inline std::string Point< T, 4 >::toString() const
{
	std::stringstream ss;

	ss << "<" << this->x << ", " <<  this->y << ", " << this->z << ", " << this->w << ">";
	return ss.str();
}



//



template< typename T, unsigned N >
inline bool operator==(const Point< T, N >& lhs, const Point< T, N >& rhs)
{
	return (memcmp(&lhs[0], &rhs[0], sizeof (T) * N) == 0 ? true : false);
}

template< typename T, unsigned N >
inline Point< T, N >								operator+(const Point< T, N >& lhs, const Point< T, N >& rhs)
{
	Point< T, N > nrv(lhs);
	nrv += rhs;
	return nrv;
}

template< typename T, unsigned N >
inline Point< T, N >								operator+(const Point< T, N >& lhs, const Vector< T, N >& rhs)
{
	Point< T, N > nrv(lhs);
	nrv += rhs;
	return nrv;
}

template< typename T, unsigned N >
inline Point< T, N >								operator-(const Point< T, N >& lhs, const Vector< T, N >& rhs)
{
	Point< T, N > nrv(lhs);
	nrv -= rhs;
	return nrv;
}

template< typename T, unsigned N >
inline Vector< T, N >								operator-(const Point< T, N >& lhs, const Point< T, N >& rhs)
{
	Vector< T, N > nrv;
	
	for (unsigned i = 0; i < N; ++i)
		nrv[i] = lhs[i] - rhs[i];
	return nrv;
}

template< typename T, unsigned N >
inline Point< T, N >								operator*(const Point< T, N >& lhs, const T& rhs)
{
	Point< T, N > nrv(lhs);
	nrv *= rhs;
	return nrv;
}

template< typename T, unsigned N >
inline Point< T, N >								operator*(const T& lhs, const Point< T, N >& rhs)
{
	Point< T, N > nrv(rhs);
	nrv *= lhs;
	return nrv;
}

template< typename T, unsigned N >
inline Point< T, N >								operator/(const Point< T, N >& lhs, const T& rhs)
{
	Point< T, N > nrv(lhs);
	nrv /= rhs;
	return nrv;
}

template< typename T, unsigned N >
inline Matrix< T, N, N >						operator*(const Point< T, N >& lhs, const Matrix< T, N, N >& rhs)
{
	Matrix< T, N, N > nrv;

	for (unsigned j = 0; j < N; ++j)
	{
		T tmp = 0;
		for (unsigned i = 0; i < N; ++i)
			tmp += lhs[i] * rhs(j, i);
		nrv(i, j) = tmp;
	}
	return nrv;
}

template< typename T, unsigned N >
inline Point< T, N >								operator*(const Matrix< T, N, N >& lhs, const Point< T, N >& rhs)
{
	Point< T, N >											nrv;
		
	for (unsigned j = 0; j < N; ++j)
	{
		T tmp = 0;
		for (unsigned i = 0; i < N; ++i)
			tmp += lhs(j, i) * rhs[i];
		nrv[j] = tmp;
	}
	return nrv;
}

	} // namespace maths
} // namespace dw

#endif // !DW_MATHS_POINT_INL
